<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<link rel="shortcut icon" href="../../share/img/logo.png">
		<link rel="stylesheet" href="../../share/css/main.css">
		<script src="../../share/js/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script>
		<script src="../../share/js/util.js" type="text/javascript" charset="utf-8"></script>
	</head>
	<body class="p-10">
		<h1>迭代器与生成器</h1>
		<div class="row">
			<div class="col-1">
				<section>
					<h3>迭代器</h3>
					<h4>概述</h4>
					<p>迭代器 iterator 是一个用于遍历集合数据的接口规范</p>
					<h4>定义</h4>
					<pre class="code">
var iterator = {
    next: function() {
        return {value:data,done:true}
    }
}
var iterable = {
    // 设置 Symbol.iterator 属性，可遍历对象
    [Symbol.iterator]: function(){
        return new iterator();
    }
}
</pre>
					<h4>要点</h4>
					<ul class="d-list">
						<li>凡是拥有实现了 iterator 接口的属性的对象都可被迭代遍历</li>
						<li>数组、Set、Map 和 字符串、NodeList、arguments 等类似数组的对象都已经默认实现了 iterator 接口</li>
						<li>对象没有默认实现，可以通过Object.keys()自定义实现</li>
						<li>iterator 要求实现 next() 方法</li>
						<li>next() 方法返回一个包含 value 属性 和 done 属性的对象</li>
						<li>value 是集合中真正的元素数据</li>
						<li>done 是布尔类型，表示是否已经遍历完成</li>
						<li>对象可通过[Sysmbol.iterator]属性指定iterator接口</li>
						<li>拥有[Symbol.iterator]属性的数据是可遍历的(iterable), 该属性是一个函数，即当前数据的遍历器生成函数</li>
						<li>遍历器生成器函数必须返回 iterator 接口对象</li>
						<li>实现了iterator接口后可用 for-of 方式遍历</li>
					</ul>
					<h4>测试</h4>
					<div id="div1">
						<button onclick="test01()">迭代数组</button>
						<button onclick="test02()">迭代对象</button>
						<button onclick="test03()">迭代Set</button>
						<button onclick="test04()">迭代Map</button>
						<button onclick="test05()">迭代字符串</button>
						<button onclick="test06()">迭代NodeList</button>
						<button onclick="test07()">迭代自定义对象</button>
						<button onclick="test08()">迭代自定义对象</button>
					</div>
				</section>
			</div>
			<div class="col-1">
				<section>
					<h3>生成器</h3>
					<h4>概述</h4>
					<p>Generator 是一个迭代器生成函数，恰好可作为对象的[Symbol.iterator]属性提供遍历接口</p>
					<h4>定义</h4>
					<pre class="code">
// 定义 generator
function * f1() {
	yield 1;
	let user = yield {name:"Jack"};
	console.log(user.name); // Tom
	return user;
}

let g1 = f1(); // 调用后返回 iterator
g1.next(); // 推荐代码执行到 yield
console.log(g1.next().value); // 接收 yield 后面表达式的值
g1.next({name:"Tom"}); // 传入参数作为上次 yield 表达式的值
					</pre>
					<h4>要点</h4>
					<ul class="d-list">
						<li>Generator 函数和普通函数基本一样，语法上有两点不同</li>
						<li>1. function 与 函数名之间有 *，位置任意</li>
						<li>2. 函数体内有 yield 或 yield *</li>
						<li>调用Generator函数后并不会立即执行内部代码，而是返回一个遍历器对象，故称为遍历器生成函数</li>
						<li>通过返回的遍历器对象，调用next()方法依次在yield 与 return 之间逐步推进执行</li>
						<li>yield暂停执行，并将其后的表达式结果作为调用next()后返回对象的value属性</li>
						<li>yield和return的区别是 done 属性值不同，前者false，后者true</li>
						<li>yield作为表达式的子表达式时，必须放在()内</li>
					</ul>
					<h4>测试</h4>
					<div>
						<button onclick="test11()">Generator</button>
						<button onclick="test12()">猜拳</button>
						<button onclick="test13()">输入输出</button>
					</div>
				</section>
				<section>
					<h3>迭代器与生成器</h3>
					<ol class="d-list">
						<li>定义 generator 函数实际上是定义了创建 iterator 对象的函数</li>
						<li>调用generator函数后，并不立即执行内部的代码，而是返回 iterator 对象</li>
						<li>调用 iterator.next() 方法，推动 generator 中代码在yield之间执行</li>
						<li>yield 后面的表达式作为 next() 返回对象的 value 属性, done 为false</li>
						<li>return 后面的表达式同样作为 next() 返回对象的 value 属性, done 为true</li>
						<li>yield 自身表达式并不返回值，或称返回undefined</li>
						<li>可通过调用next()方法时，传入参数，作为本次 yield 表达式的值</li>
						<li>第一次调用 next()，不应传入参数</li>
						<li>generator 通过 yield 传出数据，iterator 通过next()返回对象的 value接收</li>
						<li>iterator 通过next(arg)方法的参数传入数据，generator 通过 let v = yield 即 yield表达式的值接收</li>
						<li>通过 iterator.throw()可向generator传入异常，内部try catch</li>
					</ol>
				</section>
			</div>
			<div class="col-1">
				<ul id="log" class="d-list mt-10"></ul>
			</div>
		</div>
		<script>
			function test01() {
				util.clearLog();
				let foods = ['apple', 'pear', 'orange'];
				for (let item of foods) {
					util.appendLog(item);
				}
			}

			function test02() {
				util.clearLog();
				let user = {
					name: 'Tom',
					age: 20,
					gender: true
				};
				for (let k of Object.keys(user)) {
					util.appendLog(`${k}: ${user[k]}`);
				}
			}

			function test03() {
				util.clearLog();
				let s1 = new Set(['apple', 'pear', 'orange']);
				for (let item of s1) {
					util.appendLog(item);
				}
			}

			function test04() {
				util.clearLog();
				let m1 = new Map([
					['name', 'Tom'],
					['age', 20],
					['gender', true]
				]);
				for (let [k, v] of m1) {
					util.appendLog(`${k} = ${v}`);
				}
			}

			function test05() {
				util.clearLog();
				let text = "hello, world!";
				for (let item of text) {
					util.appendLog(item);
				}
			}

			function test06() {
				util.clearLog();
				let buttons = document.getElementById("div1").getElementsByTagName("button");
				for (let item of buttons) {
					util.appendLog(item.innerText);
				}
			}

			function test07() {
				let odd = {
					id: 0,
					[Symbol.iterator]: function() {
						let that = this;
						return {
							next() {
								that.id += 2;
								return {
									value: that.id,
									done: that.id > 10
								};
							},
						}
					},
				}
				util.clearLog();
				for (let item of odd) {
					util.appendLog(item);
				}
				// for (let i = 0; i < 5; i++) util.appendLog(odd.next().value);
			}

			function test08() {
				let even = {
					id: -1,
					[Symbol.iterator]: function() {
						return this;
					},
					next() {
						this.id += 2;
						return {
							value: this.id,
							done: this.id > 10
						}
					}
				}
				util.clearLog();
				for (let item of even) {
					util.appendLog(item);
				}
			}

			function test11() {
				function* g1() {
					yield 'apple';
					yield 'pear';
					yield 'orange'
				}
				util.clearLog();
				let f = g1();
				for (let v of f) {
					util.appendLog(v);
				}
			}

			function* guess1() {
				let toss = ['老虎', '杠子', '鸡', '虫'];
				let index = 0;
				for (;;) {
					yield toss[index++];
					if (index > toss.length - 1) index = 0;
				}
			}
			let fguess = guess1();

			function test12() {
				let {
					value: v,
					done: s
				} = fguess.next();
				util.appendLog(v);
			}

			function* g2(name) {
				let user = yield name;
				util.appendLog("Generator 接收到: " + user);
			}
			let fg2 = g2("Tom");

			function test13() {
				let {
					value: name
				} = fg2.next();
				util.appendLog("Generator 返回: " + name);
				// console.log(fg2.next());
				fg2.next("Jack");
			}
		</script>
	</body>
</html>
